package xusage.cumulus.deployer;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

import xusage.cumulus.spring.resolver.CumulusResourcePatternResolver;

public class CumulusStaticDeployListener implements javax.servlet.ServletContextListener {

	private Monitor monitorThread = null;
	
	@Override
	public void contextDestroyed(ServletContextEvent event) {
		monitorThread.exit();
	}

	@Override
	public void contextInitialized(ServletContextEvent event) {
		ServletContext context = event.getServletContext();
		resourceLocations = StringUtils.tokenizeToStringArray(context.getInitParameter(PARAMETER_RESOURCE_LOCATION), CONFIG_LOCATION_DELIMITERS);
		if (resourceLocations == null) {
			resourceLocations = new String[0];
		}
		String deployInterval = context.getInitParameter(PARAMETER_DEPLOY_INTERVAL);
		try {
			if (deployInterval != null) {
				this.deployInterval = Long.parseLong(deployInterval);
			}
		}
		catch (Exception ex) {
			LOGGER.warn("Deploy interval value must be an instance of java.lang.Long : {}, {}", deployInterval, ex.getMessage());
		}
		String[] deployPathDeciderClasses = StringUtils.tokenizeToStringArray(context.getInitParameter(PARAMETER_DEPLOY_DECIDER), CONFIG_LOCATION_DELIMITERS);
		for (String deployPathDeciderClass : deployPathDeciderClasses) {
			try {
				DeployPathDecider servletDeployPathDecider = (DeployPathDecider)Class.forName(deployPathDeciderClass).newInstance();
				servletDeployPathDecider.setServletContext(context);
				servletDeployPathDeciders.add(servletDeployPathDecider);
			}
			catch (Exception e) {
				LOGGER.warn("Ignore to instance class : {}, {}", deployPathDeciderClass, e.getMessage());
			}
		}
		monitorThread = new Monitor(context);
		monitorThread.start();
	}
	protected CumulusResourcePatternResolver resolver = null;
	protected void deploy(ServletContext context) {
		if (resolver == null) {
			resolver = new CumulusResourcePatternResolver(context);
		}
		for (String deployLocation : resourceLocations) {
			try {
				for (Resource resource : resolver.getResources(deployLocation)) {
					try {						
						String outputPath = decideDeployPath(resource);						
						if (outputPath != null) {
							File file = new File(outputPath);
							if (!file.exists() || file.lastModified() < resource.lastModified()){
								LOGGER.info("Deploying {} -> {}", resource.getURL(), outputPath);
								InputStream inputStream = resource.getInputStream();
								OutputStream outputStream = FileUtils.openOutputStream(new File(outputPath));
								org.apache.commons.io.IOUtils.copy(inputStream, outputStream);
								outputStream.close();
								inputStream.close();
							}
						}
						else {
							LOGGER.info("Unable to decide the deploy path : {}", resource.getURL());
						}
					}
					catch (IOException ex) {
						LOGGER.warn("Unable to deploy resource : {}, {}", resource.getURL(), ex.getMessage());
					}
				}
			}
			catch (IOException ex) {
				LOGGER.warn("Unable to load location : {}, {}", deployLocation, ex.getMessage());
			}
		}
	}
	
	protected String decideDeployPath(Resource resource) throws IOException {
		String resultPath = null;
		for (DeployPathDecider decider : servletDeployPathDeciders) {
			resultPath = decider.decide(resource);
			if (resultPath != null) {
				break;
			}
		}
		return resultPath;
	}
	
	private class Monitor extends Thread {
		
		private ServletContext context = null;
		
		public Monitor(ServletContext context) {
			this.context = context;
			setDaemon(true);
		}
		
		@Override
		public void run() {
			while (!stop) {
				try {
					deploy(context);
					if (deployInterval < 1) {
						exit();
					}
					Thread.sleep(deployInterval);
				}
				catch (Exception ex) {
					
				}
				finally {
					System.gc();
				}
			}
		}
		
		private boolean stop = false;
		public void exit() {
			stop = true;
		}
		
	}
	
	private String[] resourceLocations = null;
	
	private Long deployInterval = 0L;
	
	private final List<DeployPathDecider> servletDeployPathDeciders = new ArrayList<DeployPathDecider>();
	public void addServletDeployPathDecider(DeployPathDecider servletDeployPathDecider) {
		servletDeployPathDeciders.add(servletDeployPathDecider);
	}
	
	private static final String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
	private static final String PARAMETER_RESOURCE_LOCATION = "cumulusResourceLocation";
	private static final String PARAMETER_DEPLOY_DECIDER = "cumulusDeployDecider";
	private static final String PARAMETER_DEPLOY_INTERVAL = "cumulusDeployInterval";
	
	private static final Logger LOGGER = LoggerFactory.getLogger(CumulusStaticDeployListener.class);
}
